<?php
/**
 * | 节程 [ 节程赋能开发者，助力企业发展 ]
 * +----------------------------------------------------------------------
 *  | Copyright (c) 2020~2029 温州惊蛰网络科技有限公司 All rights reserved.
 * +----------------------------------------------------------------------
 *  | Licensed 节程并不是自由软件，未经许可不能去掉节程相关版权
 * +----------------------------------------------------------------------
 */


namespace app\index\service;

use app\index\model\Area;
use app\index\model\Commodity;
use app\index\model\Distribution;
use app\index\model\ReturnAddress;
use app\index\model\Store;
use app\index\model\UserAddress;
use app\index\util\Redis;
use think\Exception;
use think\facade\Db;

class DistributionService
{
    protected $redis;
    protected $user;
    private $config;

    public function __construct()
    {
        global $user;
        $this->redis = Redis::getInstance();
        $this->user = $user;
    }

    public function store(array $data)
    {
        $arr = [];
        $destination = [];//目的地经纬度
        $freight_arr = [];
        $address = UserAddress::find($data['user_address_id']);//目的地地址
        $address = $address['address'];
        $js = json_decode($data['commoditys'], true);
        foreach ($js as $v) {
            $hashKey = self::getHashKey($v);
            $commodityData = json_decode($this->redis->hGet(checkRedisKey("TROLLEY:" . $this->user['id']), $hashKey), true);
            //dd($commodityData);
            $find = Commodity::find($commodityData['commodityId']);
            if ($find['ft_type'] == 2) {
                $commodityData['ft_type'] = 2;
                $commodityData['ft_price'] = $find['ft_price'];
            }
            $commodityData['weight'] = $find['weight'];
           $arr[$find['merchant_id']]['stores'] = Db::name('store')
                ->where('a.mall_id', $this->user->mall_id)
                ->alias('a')
                ->where('a.merchant_id', $find['merchant_id'])
                ->whereRaw('a.district REGEXP "[' . $address . ']"')
                ->join('distribution d', 'a.id=d.store_id', 'LEFT')
                ->field('a.id as store_id,a.longitude,a.latitude,d.*')
                ->select()
                ->toArray();
            $arr[$find['merchant_id']][] = $commodityData;//合并同一商户商品
        }
      // dd($arr);
        $unified = 0;
        foreach ($arr as $kk => $vv) {
           //每个$vv都是一个订单
            $arr_weight = array_column($vv, 'weight');
            $total = array_sum($arr_weight);//商品总重量
            //dd($vv);
            $stores = $vv['stores'];
            unset($vv['stores']);
            //dd($vv);
            foreach ($vv as $kk1 => $value) {
               // dd($value['ft_type']);
                $money = 0;
                if (isset($value['ft_type']) && $value['ft_type'] == 2) {
                    $unified += $value['ft_price'];
                    unset($value[$kk1]);
                } else {
                    $money = bcadd($money, ($value['num'] * $value['price']), 2);//计算总价
                }
                
            }
            foreach ($stores as $vvv) {
                if (empty($destination)) $destination = $this->coordinate($kk, $address, $vvv['store_id']);//目的地经纬度
                if ($vvv['area'] == 1) {
                    $freight_arr[$vv['store_id']] = $this->radius_division($vvv, $destination, $money, $total, $vvv['area']) + $unified;
                } elseif ($vvv['area'] == 2) {
                    $freight_arr[$vv['store_id']] = $this->radius_division($vvv, $destination, $money, $total, $vvv['area']) + $unified;
                } elseif ($vvv['area'] == 3) {
                    $address_arr = explode(',', $data['user_address']);
                    $code = Area::where('name', $address_arr[2])->value('area_code');
                    if ($money < $vvv['starting_amount']) throw new Exception('订单金额不能小于起送金额！', HTTP_UNAUTH);
                    if (in_array($code, $vvv['area_data'])) {
                        $freight_arr[$vv['store_id']] = $vvv['distribution_fee'] + $unified;
                    }
                }
            }
        }
        return $freight_arr;
    }

    private function coordinate(int $merchant_id, string $address, int $store_id)
    {
        $this->config = Db::name('distribution')
            ->where('merchant_id', $merchant_id)
            ->where('store_id', $store_id)
            ->find();
        $url = "https://restapi.amap.com/v3/geocode/geo?key=" . $this->config['gaude_key'] . "&address=" . $address . "&output=JSON";
        $data = curl_get($url);
        //dd($data);
        return $data['geocodes']['location'];
    }

    private function getHashKey(array $data)
    {
        $str = "";
        foreach ($data as $k => $v) {
            if (!empty($v) && in_array($k, ['sku', 'id'])) $str .= $k . ":" . $v . '-';
        }
        return rtrim($str, '-');
    }

    private function distance($lat1, $lng1, $lat2, $lng2, $miles = true)
    {
        $pi80 = M_PI / 180;
        $lat1 *= $pi80;
        $lng1 *= $pi80;
        $lat2 *= $pi80;
        $lng2 *= $pi80;

        $r = 6372.797; // mean radius of Earth in km
        $dlat = $lat2 - $lat1;
        $dlng = $lng2 - $lng1;
        $a = sin($dlat / 2) * sin($dlat / 2) + cos($lat1) * cos($lat2) * sin($dlng / 2) * sin($dlng / 2);
        $c = 2 * atan2(sqrt($a), sqrt(1 - $a));
        $km = $r * $c;
        return ($miles ? ($km * 0.621371192) : $km);
    }

    private function is_ptin_poly($aLon, $aLat, $pointList = array())
    {
        $iSum = 0;
        $iCount = count($pointList);
        if ($iCount < 3) {
            return false;
        }
        foreach ($pointList as $key => $row) {
            $pLon1 = $row[0];
            $pLat1 = $row[1];
            if ($key === $iCount - 1) {
                $pLon2 = $pointList[0][0];
                $pLat2 = $pointList[0][1];
            } else {
                $pLon2 = $pointList[$key + 1][0];
                $pLat2 = $pointList[$key + 1][1];
            }
            if ((($aLat >= $pLat1) && ($aLat < $pLat2)) || (($aLat >= $pLat2) && ($aLat < $pLat1))) {
                if (abs($pLat1 - $pLat2) > 0) {
                    $pLon = $pLon1 - (($pLon1 - $pLon2) * ($pLat1 - $aLat)) / ($pLat1 - $pLat2);
                    if ($pLon < $aLon) {
                        $iSum += 1;
                    }
                }
            }
        }
        if ($iSum % 2 != 0) {
            return true;
        } else {
            return false;
        }
    }

    private function different_distance(array $config, array $origins, array $destination)
    {
        $res = $this->measure($origins, $destination);
        if ($config['distance'] > $res) {
            return $config['distribution_costs'];
        } elseif (bcsub($res, $config['distance'], 2) > $config['over_distance']) {
            return bcadd($config['over_freight'], $config['distribution_costs'], 2);
        } elseif ($config['distance'] > $res && bcsub($res, $config['distance'], 2) < $config['over_distance']) {
            return bcmul(($res - $config['distance']) / $config['second_distance'], $config['second_freight'], 2);
        }
    }

    private function measure(array $origins, array $destination)
    {
        $url = "https://restapi.amap.com/v3/distance?type=1&origins=" . $origins['longitude'] . "," . $origins['latitude'] . "&destination=" . $destination['longitude'] . "," . $destination['latitude'] . "&output=json&key=" . $this->config['gaude_key'];
        $data = curl_get($url);
        $res = json_decode($data, true);
        if ($res['status'] = 1) {
            return bcdiv($res['results']['result']['distance'], 1000, 2);
        } else {
            throw new Exception("高德返回有误！", HTTP_UNAUTH);
        }
    }

    private function radius_division(array $divide_config, array $destination, float $money, float $weight, int $type)
    {
        $res = json_decode($divide_config['divide_config'], true);
        $renewal_charge = json_decode($divide_config['renewal_charge'], true);
        if ($divide_config['divide_type'] = 1) {
            foreach ($res as $item) {
                if ($money > $item['starting_amount'] && $this->distance($item['dot']['latitude'],
                        $item['dot']['longitude'],
                        $destination['latitude'],
                        $destination['longitude']) <= $item['radius']) {
                    if ($item['is_distribution'] && $money > $item['commodity']['money']) {//满足免运费金额
                        $freight_arr[] = 0;
                        continue;
                    }
                    $freight_arr[] = $item['distribution_fee'] ?? $this->different_distance(json_decode($item['billing_method'], true),
                            [$divide_config['longitude'], $divide_config['latitude']], $destination);
                } else {
                    return [HTTP_ACCEPTED, '配送超出范围'];
                }
            }
        } elseif ($divide_config['divide_type'] = 2) {
            foreach ($res as $item) {
                $flag = $this->is_ptin_poly($destination['longitude'], $destination['latitude'], $divide_config['data']);//在区域内
                if ($flag) {
                    if ($item['is_distribution'] && $money > $item['commodity']['money']) {
                        $freight_arr[] = 0;
                        continue;
                    } else {
                        if ($money >= $item['starting_amount']) {
                            $freight_arr[] = $item['distribution_fee'] ?? $this->different_distance(json_decode($item['billing_method'], true),
                                    [$divide_config['longitude'], $divide_config['latitude']], $destination);
                        }
                    }
                }
            }
        }
        $min = min($freight_arr);
        if ($weight > $renewal_charge['weight']) {
            $min += ceil(($weight - $renewal_charge['weight']) / $renewal_charge['continuation']) * $renewal_charge['money'];
        }

        return $min;
    }
}